package com.aurelhubert.ahbottomnavigation;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.render.*;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.media.image.PixelMap;
import ohos.multimodalinput.event.TouchEvent;

import java.util.ArrayList;
import java.util.List;

public class AHBottomNavigation extends Component implements Component.DrawTask, Component.TouchEventListener {
    public enum TitleState {
        SHOW_WHEN_ACTIVE,
        SHOW_WHEN_ACTIVE_FORCE,
        ALWAYS_SHOW,
        ALWAYS_HIDE
    }

    private static final int DEFAULT_ANIMATION_DURATION = 200;

    private OnTabSelectedListener tabSelectedListener;
    private OnNavigationPositionListener navigationPositionListener;

    private static final int MIN_ITEMS = 3;
    private static final int MAX_ITEMS = 5;

    private ArrayList<AHBottomNavigationItem> items = new ArrayList<>();

    private int defaultBackgroundColor = Color.WHITE.getValue();

    private int itemActiveColor;
    private int itemInactiveColor;
    private int titleColorActive;
    private int itemDisableColor;
    private int titleColorInactive;
    private int coloredTitleColorActive;
    private int coloredTitleColorInactive;
    private int bottomNavigationHeight;
    private float selectedItemWidth, notSelectedItemWidth;
    private float offsetX;
    private boolean forceTint = true;
    private TitleState titleState = TitleState.SHOW_WHEN_ACTIVE;

    private Boolean[] itemsEnabledStates = {true, true, true, true, true};

    private boolean colored = false;

    private boolean selectedBackgroundVisible = false;

    private List<AHNotification> notifications = AHNotification.generateEmptyList(MAX_ITEMS);

    private int notificationMarginLeft;
    private int notificationMarginTop;

    private int notificationTextColor = Color.WHITE.getValue();
    private int notificationBackgroundColor = 0xffff5454;

    private int mElevation;

    private boolean isFirst = true;
    private boolean needInit = true;
    private boolean mIsHidden = false;

    private boolean selectHideNotification = false;

    private Paint mPaint = new Paint();
    private Paint elevationPaint = new Paint();
    private Paint selectBackgroundPaint = new Paint();
    private Paint mOverPaint = new Paint();
    private Paint mTextPaint = new Paint();
    private Paint notificationBackgroundPaint = new Paint();
    private Paint notificationTextPaint = new Paint();

    private int lastPosition;
    private int mPosition;
    private int downPosition;

    private AnimatorValue changeAnimatorValue;
    private AnimatorValue overAnimatorValue;
    private float changePose = 1;
    private float overPose = 1;

    private int mAnimationDuration = DEFAULT_ANIMATION_DURATION;
    private int mRippleAnimationDuration = (int) (DEFAULT_ANIMATION_DURATION * 2.5);

    private AnimatorValue showAnimatorValue;
    int showBottom = 0;
    long showDurationTime = 300;

    private int iconSize = dp2px(24);

    private int alwaysHideTop = dp2px(16);
    private int alwaysShowTopActive = dp2px(6);
    private int alwaysShowTopInactive = dp2px(8);
    private int showWhenActiveForceTopActive = dp2px(6);
    private int showWhenActiveForceTopInactive = dp2px(16);

    private int titleActiveTextSize = 0;
    private int titleInactiveTextSize = 0;

    private int defaultTitleActiveTextSize = dp2px(14);
    private int defaultTitleInactiveTextSize = dp2px(12);
    private int defaultTitleActiveSmallTextSize = dp2px(11);
    private int defaultTitleInactiveSmallTextSize = dp2px(10);

    private int textCenterY = dp2px(42);

    private boolean touchEffect;

    public AHBottomNavigation(Context context) {
        this(context, null, null);
    }

    public AHBottomNavigation(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    public AHBottomNavigation(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init(context, attrSet);
    }

    private void init(Context context, AttrSet attrSet) {
        selectedBackgroundVisible = AttrUtils.getBooleanFromAttr(attrSet, "selectedBackgroundVisible", false);

        titleColorActive = AttrUtils.getColorFromAttr(attrSet, "accentColor", 0xffFF4081);
        titleColorInactive = AttrUtils.getColorFromAttr(attrSet, "inactiveColor", 0xff747474);
        itemDisableColor = AttrUtils.getColorFromAttr(attrSet, "disableColor", 0x3A000000);

        coloredTitleColorActive = AttrUtils.getColorFromAttr(attrSet, "coloredActive", 0xffFFFFFF);
        coloredTitleColorInactive = AttrUtils.getColorFromAttr(attrSet, "coloredInactive", 0x50FFFFFF);

        colored = AttrUtils.getBooleanFromAttr(attrSet, "colored", false);

        mElevation = AttrUtils.getDimensionFromAttr(attrSet, "elevation", dp2px(8));

        notificationTextColor = Color.WHITE.getValue();
        bottomNavigationHeight = dp2px(56);

        itemActiveColor = titleColorActive;
        itemInactiveColor = titleColorInactive;

        notificationMarginLeft = dp2px(16);
        notificationMarginTop = 0;

        mPaint.setAntiAlias(true);
        elevationPaint.setAntiAlias(true);
        mOverPaint.setAntiAlias(true);
        mTextPaint.setAntiAlias(true);
        selectBackgroundPaint.setAntiAlias(true);
        notificationBackgroundPaint.setAntiAlias(true);
        notificationTextPaint.setAntiAlias(true);

        setTouchEventListener(this);
        addDrawTask(this);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (isFirst) {
            isFirst = false;
            ComponentContainer.LayoutConfig layoutConfig = getLayoutConfig();
            layoutConfig.setMarginBottom(0);
            layoutConfig.height = bottomNavigationHeight + mElevation;
            setLayoutConfig(layoutConfig);
        }
        if (items != null && items.size() > 0) {
            if (needInit) {
                needInit = false;
                getMeasurements();
            }
            drawBackground(canvas);
            drawItems(canvas);
        }
    }

    private void drawItems(Canvas canvas) {
        canvas.save();
        canvas.translate(offsetX, mElevation);
        if (selectedBackgroundVisible && (!colored || overPose == 1)) {
            selectBackgroundPaint.setColor(new Color(0x17000000));
            canvas.drawRect(
                    new RectFloat(
                            getCurrentCenterX(mPosition, mPosition) - selectedItemWidth / 2,
                            0,
                            getCurrentCenterX(mPosition, mPosition) + selectedItemWidth / 2,
                            bottomNavigationHeight),
                    selectBackgroundPaint);
        }
        for (int i = 0; i < items.size(); i++) {
            AHBottomNavigationItem bottomNavigationItem = items.get(i);
            float topOff = 0;
            if (titleState == TitleState.ALWAYS_HIDE) {
                topOff = alwaysHideTop;
            } else if (titleState == TitleState.ALWAYS_SHOW
                    || titleState == TitleState.SHOW_WHEN_ACTIVE && items.size() <= MIN_ITEMS) {
                if (i != lastPosition && i != mPosition) {
                    topOff = alwaysShowTopInactive;
                } else if (i == mPosition) {
                    topOff =
                            alwaysShowTopInactive
                                    + (alwaysShowTopActive - alwaysShowTopInactive) * changePose;
                } else if (i == lastPosition) {
                    topOff =
                            alwaysShowTopActive
                                    + (alwaysShowTopInactive - alwaysShowTopActive) * changePose;
                }
            } else {
                if (i != lastPosition && i != mPosition) {
                    topOff = showWhenActiveForceTopInactive;
                } else if (i == mPosition) {
                    topOff =
                            showWhenActiveForceTopInactive
                                    + (showWhenActiveForceTopActive - showWhenActiveForceTopInactive)
                                    * changePose;
                } else if (i == lastPosition) {
                    topOff =
                            showWhenActiveForceTopActive
                                    + (showWhenActiveForceTopInactive - showWhenActiveForceTopActive)
                                    * changePose;
                }
            }
            PixelMap pixelMap = bottomNavigationItem.getOrgPixelMap();
            float centerX =
                    getCurrentCenterX(i, lastPosition)
                            + (getCurrentCenterX(i, mPosition) - getCurrentCenterX(i, lastPosition))
                            * changePose;
            if (touchEffect && i == downPosition) {
                mPaint.setColor(new Color(0x20000000));
                canvas.drawCircle(
                        centerX, bottomNavigationHeight / 2, notSelectedItemWidth * 0.6f, mPaint);
            }

            if (forceTint) {
                if (colored) {
                    if (i == mPosition) {
                        mTextPaint.setColor(new Color(coloredTitleColorActive));
                        pixelMap = bottomNavigationItem.getColoredActivePixelMap();
                    } else if (!itemsEnabledStates[i]) {
                        mTextPaint.setColor(new Color(itemDisableColor));
                        pixelMap = bottomNavigationItem.getDisablePixelMap();
                    } else {
                        mTextPaint.setColor(new Color(coloredTitleColorInactive));
                        pixelMap = bottomNavigationItem.getColoredInActivePixelMap();
                    }
                } else {
                    if (i == mPosition) {
                        mTextPaint.setColor(new Color(titleColorActive));
                        pixelMap = bottomNavigationItem.getActivePixelMap();
                    } else if (!itemsEnabledStates[i]) {
                        mTextPaint.setColor(new Color(itemDisableColor));
                        pixelMap = bottomNavigationItem.getDisablePixelMap();
                    } else {
                        mTextPaint.setColor(new Color(titleColorInactive));
                        pixelMap = bottomNavigationItem.getInActivePixelMap();
                    }
                }
            }

            if (pixelMap != null) {
                RectFloat rectFloat = new RectFloat();
                rectFloat.left =
                        getCurrentCenterX(i, lastPosition)
                                + (getCurrentCenterX(i, mPosition) - getCurrentCenterX(i, lastPosition))
                                * changePose
                                - iconSize / 2;
                rectFloat.right =
                        getCurrentCenterX(i, lastPosition)
                                + (getCurrentCenterX(i, mPosition) - getCurrentCenterX(i, lastPosition))
                                * changePose
                                + iconSize / 2;

                rectFloat.top = topOff;
                rectFloat.bottom = topOff + iconSize;
                mPaint.setColor(new Color(0xffffffff));
                canvas.drawPixelMapHolderRect(
                        new PixelMapHolder(pixelMap),
                        new RectFloat(
                                0,
                                0,
                                pixelMap.getImageInfo().size.width,
                                pixelMap.getImageInfo().size.height),
                        rectFloat,
                        mPaint);
            }

            if (titleState != TitleState.ALWAYS_HIDE) {
                float textSize = 0;
                float alpha = mTextPaint.getAlpha();
                int activeSize = titleActiveTextSize;
                int inactiveSize = titleInactiveTextSize;
                if (activeSize == 0 || inactiveSize == 0) {
                    if (titleState == TitleState.ALWAYS_SHOW && items.size() > MIN_ITEMS) {
                        activeSize = defaultTitleActiveSmallTextSize;
                        inactiveSize = defaultTitleInactiveSmallTextSize;
                    } else {
                        activeSize = defaultTitleActiveTextSize;
                        inactiveSize = defaultTitleInactiveTextSize;
                    }
                }
                if (titleState == TitleState.ALWAYS_SHOW
                        || titleState == TitleState.SHOW_WHEN_ACTIVE && items.size() <= MIN_ITEMS) {
                    if (i != lastPosition && i != mPosition) {
                        textSize = inactiveSize;
                    } else if (i == mPosition) {
                        textSize = inactiveSize + (activeSize - inactiveSize) * changePose;
                    } else if (i == lastPosition) {
                        textSize = activeSize + (inactiveSize - activeSize) * changePose;
                    }
                } else {
                    textSize = activeSize;
                    if (i != lastPosition && i != mPosition) {
                        alpha = 0;
                    } else if (i == mPosition) {
                        alpha = changePose * alpha;
                    } else if (i == lastPosition) {
                        alpha = (1 - changePose) * alpha;
                    }
                }
                if (alpha != 0) {
                    mTextPaint.setAlpha(alpha);
                    mTextPaint.setTextSize((int) textSize);
                    float textWidth = mTextPaint.measureText(bottomNavigationItem.getTitle());
                    float textHeight = mTextPaint.descent() - mTextPaint.ascent();
                    canvas.drawText(
                            mTextPaint,
                            bottomNavigationItem.getTitle(),
                            centerX - textWidth / 2,
                            textCenterY + textHeight / 4);
                }
            }

            if (notifications != null && i < notifications.size()) {
                AHNotification notification = notifications.get(i);
                notificationBackgroundPaint.setColor(
                        new Color(
                                notification.getBackgroundColor() == 0
                                        ? notificationBackgroundColor
                                        : notification.getBackgroundColor()));
                notificationTextPaint.setColor(
                        new Color(
                                notification.getTextColor() == 0
                                        ? notificationTextColor
                                        : notification.getTextColor()));
                notificationTextPaint.setTextSize(dp2px(9));

                if (!notification.isEmpty()) {
                    float textWidth = notificationTextPaint.measureText(notification.getText());
                    float textHeight = notificationTextPaint.descent() - notificationTextPaint.ascent();
                    float notificationCenterX = centerX;
                    float marginTop = topOff + notificationMarginTop;
                    notificationCenterX = centerX + notificationMarginLeft;
                    RectFloat rectFloat = new RectFloat();
                    if (textWidth > textHeight) {
                        rectFloat.left = notificationCenterX - textWidth / 2 - dp2px(1);
                        rectFloat.right = notificationCenterX + textWidth / 2 + dp2px(1);
                    } else {
                        rectFloat.left = notificationCenterX - textHeight / 2 - dp2px(1);
                        rectFloat.right = notificationCenterX + textHeight / 2 + dp2px(1);
                    }
                    rectFloat.top = marginTop - dp2px(1);
                    rectFloat.bottom = marginTop + textHeight + dp2px(1);
                    canvas.drawRoundRect(rectFloat, dp2px(8), dp2px(8), notificationBackgroundPaint);
                    canvas.drawText(
                            notificationTextPaint,
                            notification.getText(),
                            notificationCenterX - textWidth / 2,
                            marginTop + textHeight * 3 / 4);
                }
            }
        }
        canvas.restore();
    }

    private void drawBackground(Canvas canvas) {
        LinearShader gradient =
                new LinearShader(
                        new Point[]{new Point(0, 0), new Point(0, mElevation)},
                        new float[]{0f, 0.3f, 1f},
                        new Color[]{
                                new Color(0x00000000), new Color(0x00000000), new Color(0x10000000)
                        },
                        Shader.TileMode.CLAMP_TILEMODE);

        elevationPaint.setShader(gradient, Paint.ShaderType.LINEAR_SHADER);
        if (mElevation > 0) {
            canvas.drawRect(new RectFloat(0, 0, getWidth(), mElevation), elevationPaint);
        }

        if (colored) {
            if (mPosition >= 0 && mPosition < items.size()) {
                if (overPose == 1) {
                    mPaint.setColor(new Color(items.get(mPosition).getColor()));
                } else if (lastPosition >= 0 && lastPosition < items.size()) {
                    mOverPaint.setColor(new Color(items.get(mPosition).getColor()));
                    mPaint.setColor(new Color(items.get(lastPosition).getColor()));
                }
            } else {
                mPaint.setColor(new Color(titleColorActive));
            }
        } else {
            mPaint.setColor(new Color(defaultBackgroundColor));
        }

        canvas.drawRect(
                new RectFloat(0, mElevation, getWidth(), mElevation + bottomNavigationHeight), mPaint);

        if (colored && overPose != 1) {
            canvas.saveLayer(
                    new RectFloat(0, mElevation, getWidth(), bottomNavigationHeight + mElevation),
                    mOverPaint);
            canvas.drawCircle(
                    getCurrentCenterX(mPosition, mPosition),
                    bottomNavigationHeight / 2,
                    getWidth() * overPose,
                    mOverPaint);
            canvas.restore();
        }
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            float touchX = getTouchX(touchEvent, 0);
            float touchY = getTouchY(touchEvent, 0);
            if (items != null && items.size() > 0) {
                int position = getPostionFromX(touchX);
                if (position < itemsEnabledStates.length && itemsEnabledStates[position]) {
                    downPosition = position;
                    if (touchEffect) {
                        if (overAnimatorValue == null || !overAnimatorValue.isRunning()) {
                            invalidate();
                        }
                    }
                }
            }
        } else if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_UP) {
            if (downPosition != -1) {
                float touchX = getTouchX(touchEvent, 0);
                float touchY = getTouchY(touchEvent, 0);
                if (getPostionFromX(touchX) == downPosition
                        && touchY >= 0
                        && touchY <= bottomNavigationHeight) {
                    if (items != null && items.size() > downPosition) {
                        choose(downPosition, true);
                        downPosition = -1;
                        if (touchEffect) {
                            if (overAnimatorValue == null || !overAnimatorValue.isRunning()) {
                                invalidate();
                            }
                        }
                    }
                }
            }
        } else if (touchEvent.getAction() == TouchEvent.POINT_MOVE) {
            if (downPosition != -1) {
                float touchX = getTouchX(touchEvent, 0);
                float touchY = getTouchY(touchEvent, 0);
                if (getPostionFromX(touchX) != downPosition
                        || touchY < 0
                        || touchY > bottomNavigationHeight) {
                    downPosition = -1;
                    if (items != null && items.size() > 0) {
                        if (touchEffect) {
                            if (overAnimatorValue == null || !overAnimatorValue.isRunning()) {
                                invalidate();
                            }
                        }
                    }
                }
            }
        }
        return true;
    }

    private void choose(int targetPosition, boolean useCallback) {
        if (targetPosition == mPosition || items == null || targetPosition >= items.size()) {
            return;
        }
        if (changeAnimatorValue != null && changeAnimatorValue.isRunning()) {
            return;
        }
        if (overAnimatorValue != null && overAnimatorValue.isRunning()) {
            overAnimatorValue.stop();
        }
        if (useCallback && tabSelectedListener != null) {
            tabSelectedListener.onTabSelected(downPosition, true);
        }
        lastPosition = mPosition;
        mPosition = targetPosition;
        changePose = 0;
        overPose = 0;
        //        if(items.get(mPosition) != null && items.get(mPosition).getBadgeItem() != null
        //                && items.get(mPosition).getBadgeItem().isHideOnSelect() &&
        // !items.get(mPosition).getBadgeItem().isHidden()){
        //            items.get(mPosition).getBadgeItem().hide();
        //        }

        if (selectHideNotification) {
            setNotification((AHNotification) null, targetPosition);
        }

        if (changeAnimatorValue == null) {
            changeAnimatorValue = new AnimatorValue();
            changeAnimatorValue.setDuration(mAnimationDuration);
            changeAnimatorValue.setCurveType(Animator.CurveType.LINEAR);
            changeAnimatorValue.setValueUpdateListener(
                    new AnimatorValue.ValueUpdateListener() {
                        @Override
                        public void onUpdate(AnimatorValue animatorValue, float v) {
                            changePose = v;
                        }
                    });
        }
        if (overAnimatorValue == null) {
            overAnimatorValue = new AnimatorValue();
            overAnimatorValue.setDuration(mRippleAnimationDuration);
            overAnimatorValue.setCurveType(Animator.CurveType.LINEAR);
            overAnimatorValue.setValueUpdateListener(
                    new AnimatorValue.ValueUpdateListener() {
                        @Override
                        public void onUpdate(AnimatorValue animatorValue, float v) {
                            overPose = v;
                            getContext()
                                    .getUITaskDispatcher()
                                    .asyncDispatch(
                                            new Runnable() {
                                                @Override
                                                public void run() {
                                                    invalidate();
                                                }
                                            });
                        }
                    });
        }

        changeAnimatorValue.start();
        overAnimatorValue.start();
    }

    private boolean isClassic() {
        return titleState != TitleState.ALWAYS_HIDE
                && titleState != TitleState.SHOW_WHEN_ACTIVE_FORCE
                && (items.size() == MIN_ITEMS || titleState == TitleState.ALWAYS_SHOW);
    }

    public void addItemAtIndex(int index, AHBottomNavigationItem item) {
        if (this.items.size() > MAX_ITEMS) {
            return;
        }
        if (index < items.size()) {
            item.setupPixelMap(this, iconSize);
            this.items.add(index, item);
        }
        needInit = true;
        invalidate();
    }

    public void addItem(AHBottomNavigationItem item) {
        if (this.items.size() > MAX_ITEMS) {
            return;
        }
        item.setupPixelMap(this, iconSize);
        items.add(item);
        needInit = true;
        invalidate();
    }

    public void addItems(List<AHBottomNavigationItem> items) {
        if (items.size() > MAX_ITEMS || (this.items.size() + items.size()) > MAX_ITEMS) {
            return;
        }
        for (AHBottomNavigationItem item : items) {
            item.setupPixelMap(this, iconSize);
        }
        this.items.addAll(items);
        needInit = true;
        invalidate();
    }

    public void removeItemAtIndex(int index) {
        if (index < items.size()) {
            this.items.remove(index);
            needInit = true;
            invalidate();
        }
    }

    public void removeAllItems() {
        this.items.clear();
        needInit = true;
        invalidate();
    }

    public void refresh() {
        needInit = true;
        invalidate();
    }

    public void enableItemAtPosition(int position) {
        if (position < 0 || position > items.size() - 1) {
            return;
        }
        itemsEnabledStates[position] = true;
        refresh();
    }

    public void disableItemAtPosition(int position) {
        if (position < 0 || position > items.size() - 1) {
            return;
        }
        itemsEnabledStates[position] = false;
        refresh();
    }

    public void setItemDisableColor(int itemDisableColor) {
        this.itemDisableColor = itemDisableColor;
        setupItemsPixelMaps();
        refresh();
    }

    public int getItemsCount() {
        return items.size();
    }

    public boolean isColored() {
        return colored;
    }

    public void setColored(boolean colored) {
        this.colored = colored;
        this.itemActiveColor = colored ? coloredTitleColorActive : titleColorActive;
        this.itemInactiveColor = colored ? coloredTitleColorInactive : titleColorInactive;
        needInit = true;
        refresh();
    }

    public int getDefaultBackgroundColor() {
        return defaultBackgroundColor;
    }

    public void setDefaultBackgroundColor(int defaultBackgroundColor) {
        this.defaultBackgroundColor = defaultBackgroundColor;
        invalidate();
    }

    public int getAccentColor() {
        return itemActiveColor;
    }

    public void setAccentColor(int accentColor) {
        this.titleColorActive = accentColor;
        this.itemActiveColor = accentColor;
        setupItemsPixelMaps();
        invalidate();
    }

    public int getInactiveColor() {
        return itemInactiveColor;
    }

    public void setInactiveColor(int inactiveColor) {
        this.titleColorInactive = inactiveColor;
        this.itemInactiveColor = inactiveColor;
        setupItemsPixelMaps();
        invalidate();
    }

    public void setColoredModeColors(int colorActive, int colorInactive) {
        this.coloredTitleColorActive = colorActive;
        this.coloredTitleColorInactive = colorInactive;
        setupItemsPixelMaps();
        invalidate();
    }

    public void setSelectedBackgroundVisible(boolean visible) {
        this.selectedBackgroundVisible = visible;
        invalidate();
    }

    public void setTitleTextSize(int activeSize, int inactiveSize) {
        this.titleActiveTextSize = activeSize;
        this.titleInactiveTextSize = inactiveSize;
        invalidate();
    }

    public AHBottomNavigationItem getItem(int position) {
        if (position < 0 || position > items.size() - 1) {
            return null;
        }
        return items.get(position);
    }

    public int getCurrentItem() {
        return mPosition;
    }

    public void setCurrentItem(int position) {
        setCurrentItem(position, true);
    }

    public void setCurrentItem(int position, boolean useCallback) {
        if (position >= items.size()) {
            return;
        }
        choose(position, useCallback);
    }

    public void hideBottomNavigation() {
        hideBottomNavigation(true);
    }

    public void hideBottomNavigation(boolean withAnimation) {
        if (mIsHidden) {
            return;
        }
        mIsHidden = true;
        if (showAnimatorValue == null) {
            showAnimatorValue = new AnimatorValue();
        } else if (showAnimatorValue.isRunning()) {
            showAnimatorValue.stop();
        }
        if (withAnimation) {
            int start = showBottom;
            int end = bottomNavigationHeight + mElevation;
            showAnimatorValue.setDuration(showDurationTime * (end - start) / (bottomNavigationHeight + mElevation));
            showAnimatorValue.setValueUpdateListener(
                    new AnimatorValue.ValueUpdateListener() {
                        @Override
                        public void onUpdate(AnimatorValue animatorValue, float v) {
                            showBottom = (int) (start + (end - start) * v);
                            ComponentContainer.LayoutConfig layoutConfig = getLayoutConfig();
                            layoutConfig.setMarginBottom((int) (-showBottom));
                            setLayoutConfig(layoutConfig);
                            if (navigationPositionListener != null) {
                                navigationPositionListener.onPositionChange(showBottom);
                            }
                        }
                    });
            showAnimatorValue.start();
        } else {
            ComponentContainer.LayoutConfig layoutConfig = getLayoutConfig();
            layoutConfig.setMarginBottom(-bottomNavigationHeight);
            setLayoutConfig(layoutConfig);
        }
    }

    public void restoreBottomNavigation() {
        restoreBottomNavigation(true);
    }

    public void restoreBottomNavigation(boolean withAnimation) {
        if (!mIsHidden) {
            return;
        }
        mIsHidden = false;
        if (showAnimatorValue == null) {
            showAnimatorValue = new AnimatorValue();
        } else if (showAnimatorValue.isRunning()) {
            showAnimatorValue.stop();
        }
        if (withAnimation) {
            int start = showBottom;
            int end = 0;
            showAnimatorValue.setDuration(showDurationTime * (start - end) / (bottomNavigationHeight + mElevation));
            showAnimatorValue.setValueUpdateListener(
                    new AnimatorValue.ValueUpdateListener() {
                        @Override
                        public void onUpdate(AnimatorValue animatorValue, float v) {
                            showBottom = (int) (start + (end - start) * v);
                            ComponentContainer.LayoutConfig layoutConfig = getLayoutConfig();
                            layoutConfig.setMarginBottom(-showBottom);
                            setLayoutConfig(layoutConfig);
                            if (navigationPositionListener != null) {
                                navigationPositionListener.onPositionChange(showBottom);
                            }
                        }
                    });
            showAnimatorValue.start();
        } else {
            ComponentContainer.LayoutConfig layoutConfig = getLayoutConfig();
            layoutConfig.setMarginBottom(0);
            setLayoutConfig(layoutConfig);
        }
    }

    public boolean isForceTint() {
        return forceTint;
    }

    public void setForceTint(boolean forceTint) {
        this.forceTint = forceTint;
        invalidate();
    }

    public TitleState getTitleState() {
        return titleState;
    }

    public void setTitleState(TitleState titleState) {
        this.titleState = titleState;
        needInit = true;
        invalidate();
    }

    public void setOnTabSelectedListener(OnTabSelectedListener tabSelectedListener) {
        this.tabSelectedListener = tabSelectedListener;
    }

    public void removeOnTabSelectedListener() {
        this.tabSelectedListener = null;
    }

    public void setOnNavigationPositionListener(OnNavigationPositionListener navigationPositionListener) {
        this.navigationPositionListener = navigationPositionListener;
    }

    public void removeOnNavigationPositionListener() {
        this.navigationPositionListener = null;
    }

    public void setNotification(int nbNotification, int itemPosition) {
        if (itemPosition < 0 || itemPosition > items.size() - 1) {
            return;
        }
        final String title = nbNotification == 0 ? "" : String.valueOf(nbNotification);
        notifications.set(itemPosition, AHNotification.justText(title));
        invalidate();
    }

    public void setNotification(String title, int itemPosition) {
        if (itemPosition < 0 || itemPosition > items.size() - 1) {
            return;
        }
        notifications.set(itemPosition, AHNotification.justText(title));
        invalidate();
    }

    public void setNotification(AHNotification notification, int itemPosition) {
        if (itemPosition < 0 || itemPosition > items.size() - 1) {
            return;
        }
        if (notification == null) {
            notification = new AHNotification(); // instead of null, use empty notification
        }
        notifications.set(itemPosition, notification);
        invalidate();
    }

    public void setNotificationTextColor(int textColor) {
        this.notificationTextColor = textColor;
        invalidate();
    }

    public void setNotificationBackgroundColor(int color) {
        this.notificationBackgroundColor = color;
        invalidate();
    }

    public void setNotificationMargin(int marginLeft, int marginTop) {
        this.notificationMarginLeft = marginLeft;
        this.notificationMarginTop = marginTop;
        invalidate();
    }

    public void setUseElevation(boolean useElevation) {
        mElevation = useElevation ? dp2px(8) : 0;
        invalidate();
    }

    public void setUseElevation(boolean useElevation, int elevation) {
        mElevation = useElevation ? elevation : 0;
        invalidate();
    }

    public boolean isHidden() {
        return mIsHidden;
    }

    public void setSelectHideNotification(boolean selectHideNotification) {
        this.selectHideNotification = selectHideNotification;
    }

    public boolean getSelectHideNotification() {
        return selectHideNotification;
    }

    public int getTitleColorActive() {
        return titleColorActive;
    }

    public int getTitleColorInactive() {
        return titleColorInactive;
    }

    public int getColoredTitleColorActive() {
        return coloredTitleColorActive;
    }

    public int getColoredTitleColorInactive() {
        return coloredTitleColorInactive;
    }

    public int getItemDisableColor() {
        return itemDisableColor;
    }

    public void setTouchEffect(boolean touchEffect) {
        this.touchEffect = touchEffect;
    }

    public boolean getTouchEffect() {
        return touchEffect;
    }

    private void setupItemsPixelMaps() {
        if (items != null && items.size() > 0) {
            for (AHBottomNavigationItem ahBottomNavigationItem : items) {
                ahBottomNavigationItem.setupPixelMap(this, iconSize);
            }
        }
    }

    private void getMeasurements() {
        if (isClassic()) {
            float minWidth = dp2px(104);
            float maxWidth = dp2px(169);

            if (titleState == TitleState.ALWAYS_SHOW && items.size() > MIN_ITEMS) {
                minWidth = dp2px(64);
                maxWidth = dp2px(96);
            }

            int layoutWidth = getWidth() - getPaddingLeft() - getPaddingRight();
            if (layoutWidth == 0 || items.size() == 0) {
                return;
            }

            float itemWidth = layoutWidth / items.size();
            if (itemWidth < minWidth) {
                itemWidth = minWidth;
            } else if (itemWidth > maxWidth) {
                itemWidth = maxWidth;
            }
            selectedItemWidth = itemWidth;
            notSelectedItemWidth = itemWidth;
        } else {
            float minWidth = dp2px(64);
            float maxWidth = dp2px(96);

            int layoutWidth = getWidth() - getPaddingLeft() - getPaddingRight();
            if (layoutWidth == 0 || items.size() == 0) {
                return;
            }

            float itemWidth = layoutWidth / items.size();

            if (itemWidth < minWidth) {
                itemWidth = minWidth;
            } else if (itemWidth > maxWidth) {
                itemWidth = maxWidth;
            }

            float difference = dp2px(10);
            selectedItemWidth = itemWidth + items.size() * difference;
            itemWidth -= difference;
            notSelectedItemWidth = itemWidth;

            if (titleState == TitleState.ALWAYS_HIDE) {
                selectedItemWidth = itemWidth * 1.16f;
                notSelectedItemWidth = itemWidth * 1.16f;
            }
        }

        offsetX = (getWidth() - (selectedItemWidth + notSelectedItemWidth * (items.size() - 1))) / 2;
    }

    private int getCurrentCenterX(int index, int selectIndex) {
        int x = 0;
        if (items != null && items.size() > 0) {
            for (int i = 0; i < items.size() && i <= index; i++) {
                if (index == i) {
                    if (selectIndex == i) {
                        x += selectedItemWidth / 2;
                    } else {
                        x += notSelectedItemWidth / 2;
                    }
                } else {
                    if (selectIndex == i) {
                        x += selectedItemWidth;
                    } else {
                        x += notSelectedItemWidth;
                    }
                }
            }
        }
        return x;
    }

    public interface OnTabSelectedListener {
        boolean onTabSelected(int position, boolean wasSelected);
    }

    public interface OnNavigationPositionListener {
        void onPositionChange(int y);
    }

    private int getPostionFromX(float touchX) {
        float x = offsetX;
        if (items != null && items.size() > 0) {
            for (int i = 0; i < items.size(); i++) {
                if (mPosition == i) {
                    x += selectedItemWidth;
                } else {
                    x += notSelectedItemWidth;
                }
                if (x >= touchX) {
                    return i;
                }
            }
        }
        return -1;
    }

    private float getTouchX(TouchEvent touchEvent, int index) {
        float x = 0;
        if (touchEvent.getPointerCount() > index) {
            int[] xy = getLocationOnScreen();
            if (xy != null && xy.length == 2) {
                x = touchEvent.getPointerScreenPosition(index).getX() - xy[0];
            } else {
                x = touchEvent.getPointerPosition(index).getX();
            }
        }
        return x;
    }

    private float getTouchY(TouchEvent touchEvent, int index) {
        float y = 0;
        if (touchEvent.getPointerCount() > index) {
            int[] xy = getLocationOnScreen();
            if (xy != null && xy.length == 2) {
                y = touchEvent.getPointerScreenPosition(index).getY() - xy[1];
            } else {
                y = touchEvent.getPointerPosition(index).getY();
            }
        }
        return y;
    }

    protected int dp2px(float dp) {
        return (int) (getResourceManager().getDeviceCapability().screenDensity / 160 * dp);
    }
}
